/*
 * terminals.c
 *
 *  Created on: 17/feb/2011
 *      Author: ivano
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include "terminals.h"

terminal_t* terminal_new(void) {
	terminal_t* term = (terminal_t*) malloc(sizeof(terminal_t));
	if (term == NULL)
		return NULL;
	if (pipe(term->pipes.from_daemon) < 0)
		return NULL;
	if (pipe(term->pipes.to_daemon) < 0) {
		close(term->pipes.from_daemon[0]);
		close(term->pipes.from_daemon[1]);
		return NULL;
	}
	term->pins._len_daemon = 0;
	term->pins._len_terminal = 0;
	term->pins.read = NULL;
	term->pins.write = NULL;
	term->pins.fd = NULL;
	return term;
}

terminal_t* terminal_new_socket(int sock) {
	terminal_t* term = (terminal_t*) malloc(sizeof(terminal_t));
	if (term == NULL)
		return NULL;
	term->pins.terminal_read = sock;
	term->pins.terminal_write = sock;
	term->pins.daemon_read = sock;
	term->pins.daemon_write = sock;
	term->pins._len_daemon = 0;
	term->pins._len_terminal = 0;
	term->pins.read = NULL;
	term->pins.write = NULL;
	term->pins.fd = NULL;
	return term;
}

void terminal_free(terminal_t* term) {
	if (term == NULL)
		return;
	close(term->pipes.from_daemon[0]);
	close(term->pipes.from_daemon[1]);
	close(term->pipes.to_daemon[0]);
	close(term->pipes.to_daemon[1]);
	free(term);
}

void terminal_set_iofcnts(terminal_t* term, void* fd, int (*read_fctn) (void* fd, void* buffer, int size), int (*write_fctn) (void* fd, void* buffer, int size)) {
	term->pins.fd = fd;
	term->pins.read = read_fctn;
	term->pins.write = write_fctn;
}

int terminal_write_to_daemon(terminal_t* term, char* string) {
	if (term == NULL || string == NULL)
		return 0;
	if (term->pins.write)
		term->pins.write(term->pins.fd, string, strlen(string) + 1);
	else
		return write(term->pins.terminal_write, string, strlen(string) + 1);
}

int terminal_write_to_terminal(terminal_t* term, char* string) {
	if (term == NULL || string == NULL)
		return 0;
	if (term->pins.write)
		term->pins.write(term->pins.fd, string, strlen(string) + 1);
	else
		return write(term->pins.daemon_write, string, strlen(string) + 1);
}

void _terminal_buffer_copy(char* dest, char* source, int len) {
	int i;
	for (i=0;i<len;i++)
		dest[i] = source[i];
}

void _terminal_correct_buffers(char* buffer, int* length) {
	int i;

	// Sostituisco i caratteri indesiderati
	for (i = 0; i < *length; i++) {
		switch (buffer[i]) {
			case '\r':
			case '\n':
				buffer[i] = 0;
				break;
		}
	}
	// Elimino gli zeri finali riducendo la dimensione del buffer
	for (i = *length - 1; i > 0; i--) {
		if (buffer[i] == 0 && (buffer[i-1] == 0 || buffer[i-1] == ' ' || buffer[i-1] == '\t')) {
			buffer[i-1] = 0;
			(*length)--;
		}
	}
	// Elimino gli zeri iniziali riducendo la dimensione del buffer
	for (i = 0; i < *length; i++) {
		if (buffer[i] != 0)
			break;
		else
			(*length)--;
	}
	if (i > 0)
		_terminal_buffer_copy (buffer, &buffer[i], *length);
}

void terminal_correct_buffers(terminal_t* term) {
	_terminal_correct_buffers(term->pins._buffer_daemon, &term->pins._len_daemon);
	_terminal_correct_buffers(term->pins._buffer_terminal, &term->pins._len_terminal);
}

char* terminal_read_from_daemon(terminal_t* term) {
	int res;
	char* ptr;

	terminal_correct_buffers (term);
	if (term->pins._len_terminal == 0) {
		if (term->pins.read)
			res = term->pins.read(term->pins.fd, &term->pins._buffer_terminal[term->pins._len_terminal], TERMINALS_BUFFER_SIZE - term->pins._len_terminal);
		else
			res = read(term->pins.terminal_read, &term->pins._buffer_terminal[term->pins._len_terminal], TERMINALS_BUFFER_SIZE - term->pins._len_terminal);
		if (res <= 0) {
			term->pins._len_terminal = 0;
			return NULL;
		}
		if (res > 0 && term->pins._buffer_terminal[term->pins._len_terminal + res - 1] != 0)
			term->pins._buffer_terminal[term->pins._len_terminal + res++] = 0;
		term->pins._len_terminal += res;
		terminal_correct_buffers (term);
	}
	if (term->pins._len_terminal > 0) {
		ptr = (char*) malloc(strlen(term->pins._buffer_terminal) + 1);
		strcpy (ptr, term->pins._buffer_terminal);
		_terminal_buffer_copy (term->pins._buffer_terminal, &term->pins._buffer_terminal[strlen(ptr)+1], term->pins._len_terminal - strlen(ptr) - 1);
		term->pins._len_terminal -= strlen(ptr) + 1;
		terminal_correct_buffers (term);
		return ptr;
	}
	return NULL;
}

char* terminal_read_from_terminal(terminal_t* term) {
	int res;
	char* ptr;

	terminal_correct_buffers (term);
	if (term->pins._len_daemon == 0) {
		if (term->pins.read)
			res = term->pins.read(term->pins.fd, &term->pins._buffer_daemon[term->pins._len_daemon], TERMINALS_BUFFER_SIZE - term->pins._len_daemon);
		else
			res = read(term->pins.daemon_read, &term->pins._buffer_daemon[term->pins._len_daemon], TERMINALS_BUFFER_SIZE - term->pins._len_daemon);
		if (res <= 0) {
			term->pins._len_daemon = 0;
			return NULL;
		}
		if (res > 0 && term->pins._buffer_daemon[term->pins._len_daemon + res - 1] != 0)
			term->pins._buffer_daemon[term->pins._len_daemon + res++] = 0;
		term->pins._len_daemon += res;
		terminal_correct_buffers (term);
	}
	if (term->pins._len_daemon > 0) {
		ptr = (char*) malloc(strlen(term->pins._buffer_daemon) + 1);
		strcpy (ptr, term->pins._buffer_daemon);
		_terminal_buffer_copy (term->pins._buffer_daemon, &term->pins._buffer_daemon[strlen(ptr)+1], term->pins._len_daemon - strlen(ptr) - 1);
		term->pins._len_daemon -= strlen(ptr) + 1;
		terminal_correct_buffers (term);
		return ptr;
	}
	return NULL;
}

int terminal_has_more_for_terminal(terminal_t* term) {
	int res;
	struct timeval timeout;
	fd_set fds;

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&fds);
	FD_SET(term->pins.terminal_read, &fds);

	if (select(term->pins.terminal_read + 1, &fds, NULL, NULL, &timeout) > 0)
		return 1;
	return term->pins._len_terminal;
}

int terminal_has_more_for_daemon(terminal_t* term) {
	int res;
	struct timeval timeout;
	fd_set fds;

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&fds);
	FD_SET(term->pins.daemon_read, &fds);

	if (select(term->pins.daemon_read + 1, &fds, NULL, NULL, &timeout) > 0)
		return 1;
	return term->pins._len_daemon;
}
